/*
In Section 57.3, we noted that UNIX domain datagram sockets are reliable. Write
programs to show that if a sender transmits datagrams to a UNIX domain
datagram socket faster than the receiver reads them, then the sender is eventually
blocked, and remains blocked until the receiver reads some of the pending
datagrams.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <pthread.h>
#include <unistd.h>

#include "../common/logger.h"

#define SOCKET_PATH "/tmp/bamboudp"
#define BUFFER_SIZE 10

static void *
client_thread_func(void *arg)
{
  puts("client_thread");
  int sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
  if (sfd == -1)
    log_system_error("unable to create client socket");

  struct sockaddr_un client_addr;
  memset(&client_addr, 0, sizeof(client_addr));
  client_addr.sun_family = AF_UNIX;
  snprintf(client_addr.sun_path, sizeof(client_addr.sun_path),
           "/tmp/udp_cl.%ld", (long) getpid());

  if (bind(sfd, (struct sockaddr *) &client_addr, sizeof(struct sockaddr_un))
      == -1) log_system_error("unable to bind client socket");

  /* destination address */
  struct sockaddr_un dest_addr;
  memset(&dest_addr, 0, sizeof(dest_addr));
  dest_addr.sun_family = AF_UNIX;
  strncpy(dest_addr.sun_path, SOCKET_PATH, sizeof(dest_addr.sun_path) - 1);
  for (int i = 0; i < 10000; ++i)
    {
      ssize_t len = sendto(sfd, "hello", 5, 0,
                       (struct sockaddr *) &dest_addr, sizeof(dest_addr));
      printf("client received: %d: %ld\n", i, len);
    }
  remove(client_addr.sun_path);
}

static pthread_t client_thread;

static void
activate_client(void)
{
  int s = pthread_create(&client_thread, NULL, client_thread_func, NULL);
  if (s != 0)
    log_system_error("couldn't create thread");
}

int
main(void)
{
  struct sockaddr_un server_addr;
  int sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
  if (sfd == -1)
    log_system_error("unable to create sever socket");

  if (remove(SOCKET_PATH) == -1 && errno != ENOENT)
    log_system_error("unable to remove socket file");

  memset(&server_addr, 0, sizeof(server_addr));
  server_addr.sun_family = AF_UNIX;
  strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);

  if (bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
    log_system_error("unable to bind socket");

  activate_client();

  int bytes;
  char buffer[BUFFER_SIZE];
  for (;;)
    {
      bytes = recv(sfd, buffer, BUFFER_SIZE, 0);
      if (bytes == -1)
        log_system_error("recv error");
      printf("server response: %.*s\n", bytes, buffer);
      sleep(1);
    }
  // close(sfd);

  void *result;
  pthread_join(client_thread, result);
  return EXIT_SUCCESS;
}
